﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cinchoo.Core;
using System.IO;
using Cinchoo.Core.Diagnostics;
using Cinchoo.Core.ETL;
using ChoWorkflowServer.Model;
using Cinchoo.Core.Services;
using System.Threading;

namespace ChoWorkflowServer
{
    internal static class ChoWorkflowDefManager
    {
        private readonly static object _padLock = new object();
        private readonly static Dictionary<string, string> _workflowDefFilePathCache = new Dictionary<string, string>();
        private readonly static Dictionary<string, FileSystemWatcher> _fsw = new Dictionary<string, FileSystemWatcher>();
        public static event EventHandler<ChoEventArgs<string[]>> OnWorkflowDefDiscovered;

        static ChoWorkflowDefManager()
        {
            ChoWorkflowServerSettings workflowServerSettings = new ChoWorkflowServerSettings();
            workflowServerSettings.AfterConfigurationObjectLoaded += new EventHandler<Cinchoo.Core.Configuration.ChoConfigurationObjectEventArgs>(workflowServerSettings_AfterConfigurationObjectLoaded);
        }

        private static void workflowServerSettings_AfterConfigurationObjectLoaded(object sender, Cinchoo.Core.Configuration.ChoConfigurationObjectEventArgs e)
        {
            lock (_padLock)
            {
                _workflowDefFilePathCache.Clear();
                _fsw.Clear();

                foreach (string workflowDefPath in ((ChoWorkflowServerSettings)sender).WorkflowDefPaths.SplitNTrim())
                {
                    DiscoverWorkflows(workflowDefPath);

                    FileSystemWatcher fsw = new FileSystemWatcher(workflowDefPath, "*.etl");
                    fsw.EnableRaisingEvents = true;
                    fsw.IncludeSubdirectories = false;
                    fsw.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName;
                    fsw.Changed += new FileSystemEventHandler(FSWAllChanged);
                    fsw.Created += new FileSystemEventHandler(FSWAllChanged);
                    fsw.Renamed += new RenamedEventHandler(FSWAllChanged);
                    fsw.Deleted += new FileSystemEventHandler(FSWAllChanged);
                }
            }
        }

        static void FSWAllChanged(object sender, FileSystemEventArgs e)
        {
            ChoQueuedExecutionService.Global.Enqueue(() =>
                {
                    Thread.Sleep(5000); //Give 5 secs to release the file resource by OS
                    if (Directory.Exists(e.FullPath))
                        DiscoverWorkflows(e.FullPath);
                    else
                        RefreshWorkflow(e.FullPath);

                    RaiseWorkflowDefDiscovered();
                }
            );
        }

        private static void RaiseWorkflowDefDiscovered()
        {
            EventHandler<ChoEventArgs<string[]>> onWorkflowDefDiscovered = OnWorkflowDefDiscovered;
            if (onWorkflowDefDiscovered == null) return;

            onWorkflowDefDiscovered(null, new ChoEventArgs<string[]>(WFNames));
        }

        internal static void Initialize()
        {
        }

        private static void DiscoverWorkflows(string workflowDefPath)
        {
            if (workflowDefPath.IsNullOrWhiteSpace()) return;
            if (!Directory.Exists(workflowDefPath)) return;

            lock (_padLock)
            {
                foreach (string xmlFilePath in Directory.GetFiles(workflowDefPath, "*.etl", SearchOption.AllDirectories))
                {
                    try
                    {
                        RefreshWorkflow(xmlFilePath);
                    }
                    catch (Exception ex)
                    {
                        ChoTrace.Error(ex);
                    }
                }
            }
        }

        private static void RefreshWorkflow(string xmlFilePath)
        {
            try
            {
                lock (_padLock)
                {
                    if (File.Exists(xmlFilePath))
                    {
                        string wfName = ChoETLRuntime.GetWorkflowName(xmlFilePath);
                        if (wfName.IsNullOrWhiteSpace()) return;

                        if (!_workflowDefFilePathCache.ContainsKey(wfName))
                        {
                            _workflowDefFilePathCache.Add(wfName, xmlFilePath);
                        }
                    }
                    else
                    {
                        string key = null;
                        //Remove the workflow
                        foreach (KeyValuePair<string, string> keyValuePair in _workflowDefFilePathCache)
                        {
                            if (keyValuePair.Value == xmlFilePath)
                            {
                                key = keyValuePair.Key;
                                break;
                            }
                        }

                        if (!key.IsNullOrWhiteSpace() && _workflowDefFilePathCache.ContainsKey(key))
                            _workflowDefFilePathCache.Remove(key);
                    }
                }
            }
            catch (Exception ex)
            {
                ChoTrace.Error(ex);
            }
        }

        public static string GetWorkflowDefFilePath(string taskName)
        {
            ChoGuard.ArgumentNotNullOrEmpty(taskName, "TaskName");

            return _workflowDefFilePathCache.ContainsKey(taskName) ? _workflowDefFilePathCache[taskName] : null;
        }

        public static string[] WFNames
        {
            get
            {
                lock (_padLock)
                {
                    return _workflowDefFilePathCache.Keys.ToArray();
                }
            }
        }
        
        public static string[] WFFilePaths
        {
            get
            {
                lock (_padLock)
                {
                    return _workflowDefFilePathCache.Values.ToArray();
                }
            }
        }
    }
}
